--[[

    Filename:	auxlib.lua

    Programmer:	aesiot

    Remark:

--]]

local auxlib = { _VERSION = "1.0" }

--------------------------------------------------------------------------------

function auxlib.argcheck( exp, idx, dsc, level )

	if not exp then

		error( string.format( "invalid argument #%d %s", idx, ( dsc or "" ) ), ( level or 3 ) )
	end

	return exp
end

--------------------------------------------------------------------------------

function auxlib.assert( exp, msg, level )

	if not exp then

		error( ( msg or "assertion failed!" ), ( level or 3 ) )
	end

	return exp
end

--------------------------------------------------------------------------------

function auxlib.bind1st( self, func )

	return function( ... )

		return func( self, ... )
	end
end

--------------------------------------------------------------------------------

function auxlib.if_binary( a, b, filter )

	if "function" == type( filter ) then

		return ( filter( a ) and a ) or b
	else

		return a or b
	end
end

--------------------------------------------------------------------------------

function auxlib.if_else( e, a, b )

	if e then

		return a
	else

		return b
	end
end

--------------------------------------------------------------------------------

function auxlib.is_nil( v )

	return "nil" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_boolean( v )

	return "boolean" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_number( v )

	return "number" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_string( v )

	return "string" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_table( v )

	return "table" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_userdata( v )

	return "userdata" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_function( v )

	return "function" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_thread( v )

	return "thread" == type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_integer( v )

	return "integer" == math.type( v )
end

--------------------------------------------------------------------------------

function auxlib.is_array( v )

	if "table" ~= type( v ) then

		return false
	end

	local c = 0

	for k, x in pairs( v ) do

		c = c + 1
	end

	return c == #v
end

--------------------------------------------------------------------------------

function auxlib.is_none( v )

	return nil == v or null == v
end

--------------------------------------------------------------------------------

if auxlib.is_table( debug ) and auxlib.is_function( debug.traceback ) then

	local traceback = debug.traceback

	function auxlib.pcall( func, ... )

		if nil == func then

			return false, "attempt to call a nil value"
		end

		return xpcall( func, function( errmsg )

			logger.print_multi( 4, "[ WARN ] LUA RUNTIME ERROR", 7, string.format( "  # %s", errmsg ), 7, string.format( "  # %s", traceback( nil, 2 ) ) )

			return errmsg

		end, ... )
	end
else

	function auxlib.pcall( func, ... )

		if nil == func then

			return false, "attempt to call a nil value"
		end

		return xpcall( func, function( errmsg )

			logger.print_multi( 4, "[ WARN ] LUA RUNTIME ERROR", 7, string.format( "  # %s", errmsg ) )

			return errmsg

		end, ... )
	end
end

--------------------------------------------------------------------------------

function auxlib.pcall_else( right, fault, func, ... )

	local r = { pcall( func, ... ) }

	if true == r[ 1 ] and right then

		return right( table.unpack( r, 2 ) )

	elseif false == r[ 1 ] and fault then

		return fault( table.unpack( r, 2 ) )
	else

		return table.unpack( r )
	end
end

--------------------------------------------------------------------------------

function auxlib.select( from, to, ... )

	return table.unpack( { ... }, from, to )
end

--------------------------------------------------------------------------------

function auxlib.tointeger( v, z )

	local r = ( math.tointeger( v ) or tonumber( v ) )

	local t = math.type( r )

	return ( ( "integer" == t and r ) or ( "float" == t and math.modf( r ) ) or z )
end

--------------------------------------------------------------------------------

function auxlib.try( block, catch, finally )

	auxlib.argcheck( auxlib.is_function( block ), 1, "(function expected)" )

	auxlib.argcheck( auxlib.is_function( catch ) or auxlib.is_nil( catch ), 2, "(function expected)" )

	auxlib.argcheck( auxlib.is_function( finally ) or auxlib.is_nil( finally ), 3, "(function expected)" )

	local r = { xpcall( block, catch ) }

	if finally then

		pcall( finally )
	end

	return table.unpack( r )
end

--------------------------------------------------------------------------------

function auxlib.dirent_pairs( pathname )

	local fn = function( dir )

		if not dir then

			return nil
		else

			return dir:read()
		end
	end

	return fn, os.opendir( pathname ), nil
end

function auxlib.dirent_for_each( path, proc )

	for name, attr in auxlib.dirent_pairs( path ) do

		if proc( name, attr, ( os.S_IFDIR == ( attr & os.S_IFDIR ) ) ) then

			return attr, name
		end
	end
end

function auxlib.is_directory( pathname )

	local result, mode = os.stat( pathname )

	return result and ( os.S_IFDIR == ( mode & os.S_IFDIR ) )
end

function auxlib.is_regular_file( pathname )

	local result, mode = os.stat( pathname )

	return result and ( os.S_IFREG == ( mode & os.S_IFREG ) )
end

--------------------------------------------------------------------------------

function auxlib.table_copy( t )

	if not auxlib.is_table( t ) then

		return nil
	end

	local r = {}

	for k, v in pairs( t ) do

		if auxlib.is_table( v ) then

			rawset( r, k, auxlib.table_copy( v ) )
		else

			rawset( r, k, v )
		end
	end

	return r
end

function auxlib.table_count( t )

	local r = 0

	if auxlib.is_table( t ) then

		for k, x in pairs( t ) do

			r = r + 1
		end
	end

	return r
end

function auxlib.table_for_each( t, f )

	if auxlib.is_table( t ) and auxlib.is_function( f ) then

		for k, v in pairs( t ) do

			if f( k, v ) then

				return k, v
			end
		end
	end
end

function auxlib.table_get( t, ... )

	local r = t

	for i, v in ipairs( { ... } ) do

		if not auxlib.is_table( r ) then

			return nil
		end

		r = r[ v ]
	end

	return r
end

function auxlib.table_rawget( t, ... )

	local r = t

	for i, v in ipairs( { ... } ) do

		if not auxlib.is_table( r ) then

			return nil
		end

		r = rawget( r, v )
	end

	return r
end

--------------------------------------------------------------------------------

function auxlib.array_for_each( t, f )

	if auxlib.is_table( t ) and auxlib.is_function( f ) then

		for i, v in ipairs( t ) do

			if f( v, i ) then

				return i, v
			end
		end
	end
end

function auxlib.array_max_index( t )

	local index = nil

	auxlib.table_for_each( t, function( k, v )

		if auxlib.is_integer( k ) then

			if nil == index or k > index then

				index = k
			end
		end
	end )

	return index
end

--------------------------------------------------------------------------------

function auxlib.get_args()

	local result = {}

	for index, value in ipairs( arg ) do

		if index > 1 then

			local name, data = string.match( value, "^([%w_]+)[%s=]*([%w%p%s]*)$" )

			if name and data then

				if not result[ name ] then

					result[ name ] = data

				elseif auxlib.is_table( result[ name ] ) then

					result[ name ][ #result[ name ] + 1 ] = data

				else

					result[ name ] = { result[ name ], data }
				end
			end
		end
	end

	return result
end

--------------------------------------------------------------------------------

function auxlib.json_filter( t, loop )

	local c = type( t )

	if "table" == c then

		loop = ( ( "table" == type( loop ) and loop ) or {} )

		for i, v in ipairs( loop ) do

			if v == t then

				return tostring( t )
			end
		end

		local r = {}

		table.insert( loop, t )

		if auxlib.is_array( t ) then

			for i, v in ipairs( t ) do

				r[ i ] = auxlib.json_filter( v, loop )
			end
		else

			for k, v in pairs( t ) do

				r[ tostring( k ) ] = auxlib.json_filter( v, loop )
			end
		end

		table.remove( loop )

		return r

	elseif "thread" == c then

		return tostring( t )

	elseif "function" == c then

		return string.dump( t, true )

	elseif "userdata" == c then

		return ( null == t and t ) or tostring( t )

	elseif "number" == c then

		return ( t == math.huge and "1.#INF" ) or t
	else

		return t
	end
end

function auxlib.to_json( data, mode )

	return require( "JSON" ).encode( auxlib.json_filter( data ), mode )
end

--------------------------------------------------------------------------------

function auxlib.is_lua_code( s )

	if not auxlib.is_string( s ) then

		return false

	elseif #s < 12 then

		return false
	end

	local magic, ver, fmt, data = string.unpack( "c4 B B c6", s )

	return "\x1b\x4c\x75\x61" == magic and 0x53 == ver and 0 == fmt and "\x19\x93\r\n\x1a\n" == data
end

--------------------------------------------------------------------------------

function auxlib.varargs_pairs( func, ... )

	local argv = { ... }

	for i = 1, #argv, 2 do

		func( argv[ i ], argv[ i + 1 ] )
	end
end

--------------------------------------------------------------------------------

function auxlib.new_object( metatable, ... )

	local object = {}

	if "table" == type( metatable ) and "function" == type( metatable.__init ) then

		metatable.__init( metatable, object, ... )
	end

	setmetatable( object, metatable )

	return object
end

--------------------------------------------------------------------------------

return auxlib
